﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Threading;
using FirstFloor.ModernUI.Windows.Controls;
using MergeTool.ClassLib;
using MergeTool.ClassLib.Common;
using MergeTool.ClassLib.Configuration;
using MergeTool.UI.Helpers;
using MergeTool.UI.Views;
using Microsoft.Practices.Prism.Commands;
using Microsoft.TeamFoundation.VersionControl.Client;

namespace MergeTool.UI.ViewModels
{
    public class MergeToolViewModel : MyCustomViewModelBase
    {
        private readonly RichTextBox _richTextOutput;
        private readonly FlowDocument _richTextFlowOutput;
        private readonly Dispatcher _mergeToolVewDispatcher;

        private readonly Brush _errorColor;
        private readonly Brush _passedColor;
        private readonly Brush _infoColor;

        public ICollectionView MergeApproaches { get; private set; }
        private ObservableCollection<MergeApproach> mergeApproachesObservable; 

        public BranchManager BranchManager;

        private ImmediateMerge _immediateMergeObject;

        public ImmediateMerge ImmediateMergeObject
        {
            get { return _immediateMergeObject; }
            set
            {
                this._immediateMergeObject = value;
            }
        }

        public MergeToolViewModel(FlowDocument flowDoc, RichTextBox richText, Dispatcher dispatcher)
        {
            this._richTextFlowOutput = flowDoc;
            this._richTextOutput = richText;
            this.BaselessBranchesCheck = new DelegateCommand<object>(BaselessCheck, CanSubmitMergeFlow);
            this.UnavailableBranchNamesCheck = new DelegateCommand<object>(UnavailableCheck, CanSubmitMergeFlow);
            this.NonUniqueBranchNamesCheck = new DelegateCommand<object>(NonUniqueCheck, CanSubmitMergeFlow);
            this.ChangesToMergeCheck = new DelegateCommand<object>(GetChangesToMergeCheck);
//            this.StartMerge = new DelegateCommand<object>(StartMerging, CanSubmitStartMerge);
            this.StartMerge = DelegateCommand.FromAsyncHandler(StartMerging, CanSubmitStartMerge);
            //this.StartMerge = new DelegateCommand<object>(StartMerging);
            this.mergeApproachesObservable = Settings.GetInstance().MergeApproaches;
            _errorColor = Brushes.Red;
            _passedColor = Brushes.LimeGreen;
            _infoColor = Brushes.DodgerBlue;
            this.ImmediateMergeObject = new ImmediateMerge();
            this._mergeToolVewDispatcher = dispatcher;
            MergeApproaches = new ListCollectionView(mergeApproachesObservable);
            MergeApproaches.CurrentChanged += SelectedMergeApproachChanged;
            InitializeBranchManager();
        }

        private async Task StartMerging()
        {
            var dialogView = new ConfirmationDialogView();
            dialogView.ActionNameTextBlock.Text = "Start Merge";
            var dlg = new ModernDialog
            {
                Title = "Confirm",
                Content = dialogView
            };
            dlg.Width = 250;
            dlg.Buttons = new Button[] { dlg.OkButton, dlg.CancelButton };
            dlg.ShowDialog();

            var dialogResult = dlg.DialogResult;
            if (dialogResult == false)
                return;
            IsCanSubmitMergeFlow = false;
            IsCanSubmitStartMerge = false;
            if (!IsMergeSpecific)
            {
                await Task.Run(() => this.StartMergingFlow()).ConfigureAwait(false);
            }
            else
            {
                await Task.Run(() => _immediateMergeObject.StartMergingSpecific(this.BranchManager, this)).ConfigureAwait(false);
            }

            if (!IsMergeSpecific)
            {
                IsCanSubmitMergeFlow = true;
            }
            IsCanSubmitStartMerge = true;
        }

        private async void GetChangesToMergeCheck(object obj)
        {
            MergeManager mergeManager = await Task.Run(() => new MergeManager());
            
            _richTextOutput.Document.Blocks.Clear();
            if (IsMergeSpecific)
            {
                ImmediateMergeObject.PopulatePaths(this.BranchManager);
                ImmediateMergeObject.IsShowAutoCompleteFrom = false;
                ImmediateMergeObject.IsShowAutoCompleteTo = false;
                var mergeCandidates = await Task.Run(() => mergeManager.GetMergeCandidates(ImmediateMergeObject.FromPath, ImmediateMergeObject.ToPath, ImmediateMergeObject.MergeOption, ImmediateMergeObject.ChangesetIdToStartFrom));
                //var unavailableBranches = await Task.Run(() => BranchManager.GetUnavailableBranches(MergeApproaches.CurrentItem as MergeApproach));
                Paragraph myParagraph = new Paragraph();

                if (MainViewModel.StaticIsMergeMultipleSpecifiedChangesets)
                {
                    mergeCandidates = mergeManager.ParseMultipleChangesetsTextBox(MainViewModel.StaticExplicitChangesetArray, mergeCandidates);
                }

                //ImmediateMergeObject.ChangesetId
                if (mergeCandidates.Length >= 1)
                {
                    myParagraph.Inlines.Add(new Run("Following changesets are available for merging: "));
                    myParagraph.Margin = new Thickness(10,20,10,20);
                    _richTextFlowOutput.Blocks.Add(myParagraph);

                    CreateTableForMergeCandidates(mergeCandidates);

                }
                else
                {
                    myParagraph.Inlines.Add((GetColorizedRun("There are no changes to merge.", _passedColor)));
                    _richTextFlowOutput.Blocks.Add(myParagraph);
                }
                
            }
            else
            {
                List<BranchPair> branchesFullPathPairs = null;
                
                var mergeApproach = MergeApproaches.CurrentItem as MergeApproach;
                if (mergeApproach != null)
                {
                    branchesFullPathPairs =  await Task.Run(() => BranchManager.GetFullBranchPathForMergeApproach(mergeApproach.Name));
                }
                foreach (var branchPair in branchesFullPathPairs)
                {
                    Paragraph myParagraph = new Paragraph();
                    var mergeCandidates = await Task.Run(() => mergeManager.GetMergeCandidates(branchPair.BranchNameFrom, branchPair.BranchNameTo, branchPair.MergeOption, branchPair.ChangesetIdToStartFrom));

                    if (MainViewModel.StaticIsMergeMultipleSpecifiedChangesets)
                    {
                        mergeCandidates = mergeManager.ParseMultipleChangesetsTextBox(MainViewModel.StaticExplicitChangesetArray, mergeCandidates);
                    }

                    if (mergeCandidates.Length < 1)
                    {
                        myParagraph.Inlines.Add(
                            (GetColorizedRun("There are no changes to merge between ",_passedColor)));
                        myParagraph.Inlines.Add(GetRunWithSettings(branchPair.BranchNameFrom, _passedColor, FontWeights.Bold));
                        myParagraph.Inlines.Add(GetColorizedRun(" and ", _passedColor));
                        myParagraph.Inlines.Add(GetRunWithSettings(branchPair.BranchNameTo+"\n", _passedColor, FontWeights.Bold));
                        _richTextFlowOutput.Blocks.Add(myParagraph);
                    }
                    else
                    {
                        myParagraph.Inlines.Add(new Run("From branch: "));

                        myParagraph.Inlines.Add(GetColorizedRun(branchPair.BranchNameFrom,
                        _infoColor));

                        myParagraph.Inlines.Add(new Run(" to branch: "));

                        myParagraph.Inlines.Add(GetColorizedRun(branchPair.BranchNameTo,
                        _infoColor));

                        myParagraph.Inlines.Add(new Run(" are following changesets available for merging:"));
                        _richTextFlowOutput.Blocks.Add(myParagraph);
                        CreateTableForMergeCandidates(mergeCandidates);
                    }
                    
                }
             }
        }

        public void CreateTableForMergeCandidates(MergeCandidate [] mergeCandidates)
        {
            var table = new Table();
            table.Margin = new Thickness(10, 5, 10, 5);
            _richTextFlowOutput.Blocks.Add(table);

            table.BorderThickness = new Thickness(1);
            table.BorderBrush = Brushes.Black;
            table.CellSpacing = 0;
            int numberOfColumns = 4;

            for (int i = 0; i < numberOfColumns; i++)
            {
                table.Columns.Add(new TableColumn());


            }

            table.Columns[0].Width = new GridLength(80);
            table.Columns[2].Width = new GridLength(115);
            table.Columns[3].Width = new GridLength(155);

            table.RowGroups.Add(new TableRowGroup());

            table.RowGroups[0].Rows.Add(new TableRow());

            TableRow currentRow = table.RowGroups[0].Rows[0];

            currentRow.FontSize = 12;
            currentRow.FontWeight = System.Windows.FontWeights.Bold;

            currentRow.Cells.Add(new TableCell(new Paragraph(new Run("Changeset Id"))));
            currentRow.Cells.Add(new TableCell(new Paragraph(new Run("Comment"))));
            currentRow.Cells.Add(new TableCell(new Paragraph(new Run("Checked-In by"))));
            currentRow.Cells.Add(new TableCell(new Paragraph(new Run("When"))));

            foreach (var cell in currentRow.Cells)
            {
                cell.TextAlignment = TextAlignment.Center;

                cell.BorderBrush = Brushes.Black;
            }

            currentRow.Cells[0].BorderThickness = new Thickness(1, 1, 0, 0);
            currentRow.Cells[1].BorderThickness = new Thickness(0, 1, 0, 0);
            currentRow.Cells[2].BorderThickness = new Thickness(0, 1, 0, 0);
            currentRow.Cells[3].BorderThickness = new Thickness(0, 1, 1, 0);

            currentRow.Background = Brushes.AliceBlue;
            var thickness = new Thickness(10, 3, 10, 3);
            for (int i = 0; i < mergeCandidates.Length; i++)
            {

                table.RowGroups[0].Rows.Add(new TableRow());
                currentRow = table.RowGroups[0].Rows[i + 1];

                currentRow.FontSize = 12;
                currentRow.FontWeight = FontWeights.Normal;

                currentRow.Cells.Add(new TableCell(new Paragraph(GetRunWithSettings(mergeCandidates[i].Changeset.ChangesetId.ToString(),
                    Brushes.Black, FontWeights.Bold))
                {
                    Margin = thickness
                }));
                currentRow.Cells.Add(new TableCell(new Paragraph(GetColorizedRun(mergeCandidates[i].Changeset.Comment,
                    Brushes.Black))
                {
                    Margin = thickness
                }));
                currentRow.Cells.Add(new TableCell(new Paragraph(GetColorizedRun(mergeCandidates[i].Changeset.OwnerDisplayName,
                    Brushes.Black))
                {
                    Margin = thickness
                }));
                currentRow.Cells.Add(new TableCell(new Paragraph(GetColorizedRun(mergeCandidates[i].Changeset.CreationDate.ToString("M/d/yyyy H:mm:ss tt"),
                    Brushes.Black))
                {
                    Margin = thickness
                }));


                currentRow.Cells[0].TextAlignment = TextAlignment.Center;

                currentRow.Cells[2].TextAlignment = TextAlignment.Center;
                currentRow.Cells[3].TextAlignment = TextAlignment.Center;
                for (int p = 0; p < 4; p++)
                {
                    if (i == 0)
                    {
                        currentRow.Cells[p].BorderThickness = new Thickness(1, 2, 1, 1);
                    }
                    else
                    {
                        currentRow.Cells[p].BorderThickness = new Thickness(1, 1, 1, 1);
                    }

                    currentRow.Cells[p].BorderBrush = Brushes.Black;
                }

            }
        }

        private bool CanSubmitStartMerge()
        {
            return IsCanSubmitStartMerge;
        }

        private bool _isCanSubmitStartMerge;

        public bool IsCanSubmitStartMerge
        {
            get { return _isCanSubmitStartMerge; }
            set
            {
                SetProperty(ref this._isCanSubmitStartMerge, value);
                this.StartMerge.RaiseCanExecuteChanged();
            }
        }

        private bool _isMergeSpecific;

        public bool IsMergeSpecific
        {
            get { return this._isMergeSpecific; }
            set
            {
                SetProperty( ref this._isMergeSpecific, value);
                this._isMergeSpecific = value;
                if (BranchManager != null && value==false)
                {
                    IsCanSubmitMergeFlow = true;
                    IsCanSubmitStartMerge = true;
                }else if (value)
                {
                    IsCanSubmitMergeFlow = false;
                    IsCanSubmitStartMerge = true;
                }
                else
                {
                    IsCanSubmitMergeFlow = false;
                    IsCanSubmitStartMerge = false;
                }
            }
        }

        private bool _isCanSubmitMergeFlow;

        public bool IsCanSubmitMergeFlow
        {
            get { return this._isCanSubmitMergeFlow; }
            set
            {
                SetProperty(ref _isCanSubmitMergeFlow, value);
                _isCanSubmitMergeFlow = value;
                RaiseCanExecuteForAll();
            }
        }

        private bool CanSubmitMergeFlow(object arg)
        {
            return IsCanSubmitMergeFlow;
        }


        public async void InitializeBranchManager()
        {
            this.IsCanSubmitMergeFlow = false;

            if(BranchManager==null)
                this.BranchManager = await Task.Run(() => new BranchManager());

            this.IsCanSubmitMergeFlow = true;
            IsCanSubmitStartMerge = true;
        }

        public void RaiseCanExecuteForAll()
        {
            this.BaselessBranchesCheck.RaiseCanExecuteChanged();
            this.UnavailableBranchNamesCheck.RaiseCanExecuteChanged();
            this.NonUniqueBranchNamesCheck.RaiseCanExecuteChanged();
            //this.StartMerge.RaiseCanExecuteChanged();
        }

        private async Task StartMergingFlow()
        {
            
            
            List<BranchPair> branchesFullPathPairs = null;
            var settings = Settings.GetInstance();
            
            var mergeApproach = MergeApproaches.CurrentItem as MergeApproach;
            if (mergeApproach != null)
            {
                branchesFullPathPairs = BranchManager.GetFullBranchPathForMergeApproach(mergeApproach.Name);
            }
            List<int> checkedInChangesets = new List<int>();
            PrintMergingStarted();
            foreach (var branchPair in branchesFullPathPairs)
            {
                MergeManager mergeManager = new MergeManager();
                if(settings.IsMergeByChangesets || MainViewModel.StaticIsMergeMultipleSpecifiedChangesets)
                {
                    PrintBranchMergeBegin(branchPair.BranchNameFrom, branchPair.BranchNameTo);
                    MergeObjectLink mergeObjectLink = new MergeObjectLink();
                    try
                    {
                        var mergeCandidates = mergeManager.GetMergeCandidates(branchPair.BranchNameFrom, branchPair.BranchNameTo, branchPair.MergeOption, branchPair.ChangesetIdToStartFrom);

                        if (MainViewModel.StaticIsMergeMultipleSpecifiedChangesets)
                        {
                            var parsedMergeCandidates = mergeManager.ParseMultipleChangesetsTextBox(MainViewModel.StaticExplicitChangesetArray, mergeCandidates);
                            if (!parsedMergeCandidates.Any() && checkedInChangesets.Any())
                            {
                                    mergeCandidates = (from mC in mergeCandidates
                                                 where checkedInChangesets.Contains(mC.Changeset.ChangesetId)
                                                 orderby mC.Changeset.ChangesetId
                                                 select mC).ToArray();
                                    checkedInChangesets.Clear();
                            }
                            else
                            {
                                mergeCandidates = parsedMergeCandidates;
                            }
                        }

                        foreach (MergeCandidate t in mergeCandidates)
                        {

                            PrintChangesetMergeBegin(t.Changeset.ChangesetId.ToString(), branchPair.BranchNameFrom,
                                branchPair.BranchNameTo);

                            mergeObjectLink = mergeManager.MergeByChangeset(branchPair.BranchNameFrom,
                                branchPair.BranchNameTo, t.Changeset, branchPair.MergeOption, branchPair.CheckinCommentPattern, branchPair.ChangesetIdToStartFrom);

                            this.ProceedAfterMerge(mergeObjectLink, mergeManager);

                            if (MainViewModel.StaticIsMergeMultipleSpecifiedChangesets && mergeObjectLink.ChangesetIdAfterCheckin > 0)
                            {
                                checkedInChangesets.Add(mergeObjectLink.ChangesetIdAfterCheckin);
                            }

                            PrintChangesetMergeEnd(t.Changeset.ChangesetId.ToString(), branchPair.BranchNameFrom,
                                branchPair.BranchNameTo, mergeObjectLink);

                        }

                    }
                    catch (Exception ex)
                    {
                        PrintErrorDuringMerge(ex);
                        break;
                    }
                    PrintBranchMergeEnd(branchPair.BranchNameFrom, branchPair.BranchNameTo, mergeObjectLink);
                }
                else
                {
                    try
                    {
                        PrintBranchMergeBegin(branchPair.BranchNameFrom, branchPair.BranchNameTo);

                        MergeObjectLink mergeObjectLink = mergeManager.MergeByBranch(branchPair.BranchNameFrom,
                            branchPair.BranchNameTo, branchPair.MergeOption, branchPair.CheckinCommentPattern, branchPair.ChangesetIdToStartFrom);

                        this.ProceedAfterMerge(mergeObjectLink, mergeManager);

                        PrintBranchMergeEnd(branchPair.BranchNameFrom, branchPair.BranchNameTo, mergeObjectLink);
                    }
                    catch (Exception ex)
                    {
                        PrintErrorDuringMerge(ex);
                        break;
                    }
                }
            }
            PrintMergingEnded();
        }

        public void ProceedAfterMerge(MergeObjectLink mergeObjectLink,MergeManager mergeManager)
        {
            if (mergeObjectLink != null)
            {

                do
                {
                    if (Settings.GetInstance().IsAutomaticVersionIncrease)
                    {
                        mergeObjectLink = this.ResolveConflictsDuringMergeVersionIncrease(mergeObjectLink);
                    }
                    else
                    {
                        mergeObjectLink = mergeManager.ResolveConflictsDuringMerge(mergeObjectLink);
                    }

                    if (!string.IsNullOrEmpty(mergeObjectLink.InterruptMessage))
                    {
                        var dialogResult = ShowConfirmationDialogDispatcher("Cancel conflict resolution.");
                        if (dialogResult)
                        {
                            mergeObjectLink.TempWorkspace.Dispose();
                            break;
                        }
                    }
                    else
                    {
                        break;
                    }
                } while (true);

                

                if (!string.IsNullOrEmpty(mergeObjectLink.InterruptMessage))
                {
                    this.PrintMessageDuringMerge(mergeObjectLink.InterruptMessage);
                }
                else
                {
                    do
                    {
                        mergeObjectLink = mergeManager.PerformCheckIn(mergeObjectLink);

                        if (!string.IsNullOrEmpty(mergeObjectLink.InterruptMessage))
                        {
                            var dialogResult = ShowConfirmationDialogDispatcher("Cancel check-in.");
                            if (dialogResult)
                            {
                                this.PrintMessageDuringMerge(mergeObjectLink.InterruptMessage);
                                mergeObjectLink.TempWorkspace.Dispose();
                                break;
                            }
                        }
                        else
                        {
                            break;
                        }
                    } while (true);

                }
            }
            else
            {
                this.PrintMessageDuringMerge("No candidates for merging found.");
            }
        }

        public bool ShowConfirmationDialogDispatcher(string text)
        {
            bool? retValue = null;
            this._mergeToolVewDispatcher.Invoke(() =>
            {
                var dialogView = new ConfirmationDialogView();
                dialogView.ActionNameTextBlock.Text = text;
                var dlg = new ModernDialog
                {
                    Title = "Confirm",
                    Content = dialogView
                };
                dlg.Width = 250;
                dlg.Buttons = new Button[] {dlg.YesButton, dlg.NoButton};
                dlg.ShowDialog();

                var dialogResult = dlg.DialogResult;
                retValue = dialogResult;
            });

            if (retValue.Equals(null) || retValue.Equals(false))
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        private async void UnavailableCheck(object obj)
        {
            _richTextOutput.Document.Blocks.Clear();
            var unavailableBranches = await Task.Run(() =>  BranchManager.GetUnavailableBranches(MergeApproaches.CurrentItem as MergeApproach));
            Paragraph myParagraph = new Paragraph();
            if (unavailableBranches.Any())
            {
                myParagraph.Inlines.Add(new Run("Branch/es: \n"));
                for (int i = 0; i < unavailableBranches.Count; i++)
                {
                    myParagraph.Inlines.Add(GetColorizedRun(unavailableBranches[i].ToString(), _errorColor));
                    if (i < unavailableBranches.Count - 1)
                    {
                        myParagraph.Inlines.Add(new Run(", "));
                    }
                }
                myParagraph.Inlines.Add(new Run(" are not available in the source control!"));
            }
            else
            {
                PrintCheckSucceededMessage(myParagraph, "unavailable branches");
            }
            _richTextFlowOutput.Blocks.Add(myParagraph);
        }

        private async void NonUniqueCheck(object obj)
        {
            
            _richTextOutput.Document.Blocks.Clear();
            Paragraph myParagraph = new Paragraph();
            var nonUniqueBranches = await Task.Run(() => BranchManager.GetNonUniqueBranches(MergeApproaches.CurrentItem as MergeApproach));
            if (nonUniqueBranches.Any())
            {
                myParagraph.Inlines.Add(new Run("Branch/es: \n"));
                for (int i = 0; i < nonUniqueBranches.Count; i++)
                {
                    myParagraph.Inlines.Add(GetColorizedRun(nonUniqueBranches[i], _errorColor));
                    if (i < nonUniqueBranches.Count - 1)
                    {
                        myParagraph.Inlines.Add(new Run(", "));
                    }
                }
                myParagraph.Inlines.Add(new Run(" are not uniquely specified within the configuration!"));
            }
            else
            {
                PrintCheckSucceededMessage(myParagraph, "non unique branch names");
            }
            _richTextFlowOutput.Blocks.Add(myParagraph);
        }

        private void SelectedMergeApproachChanged( object sender, EventArgs e )
        {
            MergeApproach current = MergeApproaches.CurrentItem as MergeApproach;
        }


        private async void BaselessCheck(object obj)
        {
            _richTextOutput.Document.Blocks.Clear();
            var baselessBranchesPairs = await Task.Run(() => BranchManager.GetBaselessMergeBranches(MergeApproaches.CurrentItem as MergeApproach));
            Paragraph myParagraph = new Paragraph();
            if (baselessBranchesPairs.Any())
            {
                foreach (var baselessBranchesPair in baselessBranchesPairs)
                {

                    myParagraph.Inlines.Add(new Run("Branch "));
                    myParagraph.Inlines.Add(GetColorizedRun(baselessBranchesPair.BranchNameFrom, _errorColor));
                    myParagraph.Inlines.Add(new Run(" and "));
                    myParagraph.Inlines.Add(GetColorizedRun(baselessBranchesPair.BranchNameTo, _errorColor));
                    myParagraph.Inlines.Add(new Run(" with order number "));
                    myParagraph.Inlines.Add(GetColorizedRun(baselessBranchesPair.Order.ToString(), _errorColor));
                    myParagraph.Inlines.Add(new Run(" would result into baseless merge!"));
                    myParagraph.Inlines.Add("\n \n");
                }
            }
            else
            {
                PrintCheckSucceededMessage(myParagraph,"baseless merge branches");
            }
            
            _richTextFlowOutput.Blocks.Add(myParagraph);
        }

        public void PrintCheckSucceededMessage(Paragraph p, string text)
        {
            p.Inlines.Add(GetColorizedRun("Check for " + text + " passed.", _passedColor));
            _richTextFlowOutput.Blocks.Add(p);
        }

        public Run GetColorizedRun(string text, Brush color)
        {
            Run colorizedRun = new Run(text);
            colorizedRun.Foreground = color;
            colorizedRun.FontWeight = FontWeights.Medium;
            return colorizedRun;
        }

        public Run GetRunWithSettings(string text, Brush color, FontWeight fontWeight)
        {
            Run run = new Run(text);
            if (color != null)
            {
                run.Foreground = color;
            }
            if (fontWeight != FontWeights.Normal)
            {
                run.FontWeight = fontWeight;
            }
            return run;
        }



        public void PrintChangesetMergeBegin(string changesetId, string fromPath, string toPath)
        {
            this._mergeToolVewDispatcher.Invoke(() =>
            {
                var block = this._richTextFlowOutput.Blocks;
                Paragraph p = (Paragraph)block.LastBlock;
                p.Inlines.Add(GetRunWithSettings("\n\tMerging changeset: ", null, FontWeights.Normal));
                p.Inlines.Add(GetRunWithSettings(changesetId, null, FontWeights.Medium));
                p.Inlines.Add(GetRunWithSettings(" from: ", null, FontWeights.Normal));
                p.Inlines.Add(GetRunWithSettings(fromPath, null, FontWeights.Medium));
                p.Inlines.Add(GetRunWithSettings(" to: ", null, FontWeights.Normal));
                p.Inlines.Add(GetRunWithSettings(toPath, null, FontWeights.Medium));
                p.Inlines.Add(GetRunWithSettings(".", null, FontWeights.Normal));
            });
        }

        public void PrintBranchMergeBegin(string fromPath, string toPath)
        {
            this._mergeToolVewDispatcher.Invoke(() =>
            {
                var block = this._richTextFlowOutput.Blocks;
                Paragraph p = (Paragraph)block.LastBlock;
                p.Inlines.Add(GetRunWithSettings("\n    Merging branch: ", null, FontWeights.Normal));
                p.Inlines.Add(GetRunWithSettings(fromPath, _infoColor, FontWeights.Medium));
                p.Inlines.Add(GetRunWithSettings(" into: ", null, FontWeights.Normal));
                p.Inlines.Add(GetRunWithSettings(toPath, _infoColor, FontWeights.Medium));
                p.Inlines.Add(GetRunWithSettings(".", null, FontWeights.Normal));
            });
        }

        public void PrintChangesetMergeEnd(string changesetId, string fromPath, string toPath, MergeObjectLink mergeObjectLink)
        {
            this._mergeToolVewDispatcher.Invoke(() =>
            {
                var block = this._richTextFlowOutput.Blocks;
                Paragraph p = (Paragraph)block.LastBlock;
                p.Inlines.Add(GetRunWithSettings("\n\tFinished merging changeset: ", null, FontWeights.Normal));
                p.Inlines.Add(GetRunWithSettings(changesetId, null, FontWeights.Medium));
                if (mergeObjectLink.ChangesetIdAfterCheckin > 0)
                {
                    p.Inlines.Add(GetRunWithSettings(", changeset id created by this merge operation: ", null, FontWeights.Normal));
                    p.Inlines.Add(GetRunWithSettings(mergeObjectLink.ChangesetIdAfterCheckin.ToString(), null, FontWeights.Medium));
                    mergeObjectLink.ChangesetIdAfterCheckin = -1;
                }
                p.Inlines.Add(GetRunWithSettings(".", null, FontWeights.Normal));
            });
        }

        public void PrintBranchMergeEnd(string fromPath, string toPath, MergeObjectLink mergeObjectLink)
        {
            this._mergeToolVewDispatcher.Invoke(() =>
            {
                var block = this._richTextFlowOutput.Blocks;
                Paragraph p = (Paragraph)block.LastBlock;
                p.Inlines.Add(GetRunWithSettings("\n    Finished merging branches: ", null, FontWeights.Normal));
                p.Inlines.Add(GetRunWithSettings(fromPath, _infoColor, FontWeights.Medium));
                p.Inlines.Add(GetRunWithSettings(" and ", null, FontWeights.Normal));
                p.Inlines.Add(GetRunWithSettings(toPath, _infoColor, FontWeights.Medium));
                if (mergeObjectLink != null && mergeObjectLink.ChangesetIdAfterCheckin > 0)
                {
                    p.Inlines.Add(GetRunWithSettings(", changeset id created by this merge operation: ", null, FontWeights.Normal));
                    p.Inlines.Add(GetRunWithSettings(mergeObjectLink.ChangesetIdAfterCheckin.ToString(), _infoColor, FontWeights.Medium));
                    mergeObjectLink.ChangesetIdAfterCheckin = -1;
                }
                p.Inlines.Add(GetRunWithSettings(".\n", null, FontWeights.Normal));
            });
        }

        public void PrintMessageDuringMerge(string message)
        {
            this._mergeToolVewDispatcher.Invoke(() =>
            {
                var block = this._richTextFlowOutput.Blocks;
                Paragraph p = (Paragraph)block.LastBlock;
                p.Inlines.Add(GetRunWithSettings("\n\t"+message, Brushes.Black, FontWeights.DemiBold));
            });
        }

        public void PrintMessageDuringMerge(string message, Brush color)
        {
            this._mergeToolVewDispatcher.Invoke(() =>
            {
                var block = this._richTextFlowOutput.Blocks;
                Paragraph p = (Paragraph)block.LastBlock;
                p.Inlines.Add(GetRunWithSettings("\n\t" + message, color, FontWeights.DemiBold));
            });
        }

        public void PrintErrorDuringMerge(Exception ex)
        {
            this._mergeToolVewDispatcher.Invoke(() =>
            {
                var block = this._richTextFlowOutput.Blocks;
                Paragraph p = (Paragraph)block.LastBlock;
                p.Inlines.Add(GetRunWithSettings("\n Error has occured during the merging! ", _errorColor, FontWeights.Bold));
                p.Inlines.Add(GetRunWithSettings("\n Details: " + ex.Message, _errorColor, FontWeights.DemiBold));
                p.Inlines.Add(GetRunWithSettings("\n\nInnerException:" + ex.InnerException + "Stack trace: " + ex.StackTrace,Brushes.DarkOrange, FontWeights.Normal));
            });
        }

        public void PrintMergingEnded()
        {
            this._mergeToolVewDispatcher.Invoke(() =>
            {
                Paragraph p = new Paragraph();
                p.Inlines.Add(GetRunWithSettings("Done...", null, FontWeights.Bold));
                _richTextFlowOutput.Blocks.Add(p);
            });
        }

        public void PrintMergingStarted()
        {
            this._mergeToolVewDispatcher.Invoke(() =>
            {
                this._richTextFlowOutput.Blocks.Clear();
                Paragraph p = new Paragraph();
                p.Inlines.Add(GetRunWithSettings("Starting...", null, FontWeights.Bold));
                _richTextFlowOutput.Blocks.Add(p);
            });
        }

        public MergeObjectLink ResolveConflictsDuringMergeVersionIncrease(MergeObjectLink mergeObjectLink)
        {
            CheckinEvaluationResult cer;
            cer = mergeObjectLink.TempWorkspace.Workspace.EvaluateCheckin2(CheckinEvaluationOptions.All, mergeObjectLink.TempWorkspace.Workspace.GetPendingChanges(), mergeObjectLink.Comment, null, mergeObjectLink.WorkItemsToAssociate.ToArray());

            foreach (CheckinConflict cc in cer.Conflicts)
            {
                mergeObjectLink.IsConflictHasOccured = true;
                TfsConnection connection = TfsConnection.GetInstance();
                if (!cc.Resolvable)
                {
                    throw new Exception("Unresolvable conflict has occured! Details: " + cc.Message);
                }

                if (cc.ServerItem.ToLower().EndsWith(@"/assemblyinfo.cs") ||
                    cc.ServerItem.ToLower().EndsWith(@".vbp") || cc.ServerItem.ToLower().EndsWith(@".rc") ||
                    cc.ServerItem.ToLower().EndsWith(@".sql"))
                {
                    Conflict[] cs = mergeObjectLink.TempWorkspace.Workspace.QueryConflicts(new[] { cc.ServerItem }, false);

                    this.PrintMessageDuringMerge(string.Format("Attempting to auto resolve conflicts in version number in {0}", cc.ServerItem));

                    foreach (Conflict c in cs)
                    {
                        Encoding encoding = Encoding.GetEncoding(c.TheirEncoding);
                        if (c.YourEncoding != c.TheirEncoding)
                        {
                            this.PrintMessageDuringMerge("Encoding in source and target differs, skipping",Brushes.Red);
                            continue;
                        }
                        // boolean variable to indicate if the automatic version increase has failed - if this is set, file will be merged manually by the user
                        bool effedup = false;
                        // first try to merge the conflict automatically
                        try
                        {
                            mergeObjectLink.TempWorkspace.Workspace.MergeContent(c, false);
                        }
                        catch (Exception)
                        {
                            mergeObjectLink.TempWorkspace.Workspace.MergeContent(c, true);
                        }
                            

                        if (c.ContentMergeSummary.TotalConflicting > 0)
                        {
                            try
                            {
                                string tmpOutResultFile = Path.GetTempFileName();
                                string tmpOutFile = Path.GetTempFileName();
                                string tmpTheirFile = Path.GetTempFileName();
                                string tmpYourFile = Path.GetTempFileName();
                                string tmpBaseFile = Path.GetTempFileName();
                                c.DownloadBaseFile(tmpBaseFile);
                                c.DownloadTheirFile(tmpTheirFile);
                                c.DownloadYourFile(tmpYourFile);

                                // run Beyond Compare first. This gives us the resulting merged file with indicated conflicts
                                Process bcomp = new Process();
                                bcomp.StartInfo.FileName = Settings.GetInstance().BeyondComparePath;
                                bcomp.StartInfo.Arguments = String.Format(@"/automerge /force ""{0}"" ""{1}"" ""{2}"" ""{3}""", tmpTheirFile, tmpYourFile, tmpBaseFile, tmpOutFile);
                                bcomp.StartInfo.CreateNoWindow = true;
                                bcomp.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
                                bcomp.Start();
                                bcomp.WaitForExit();

                                string line;
                                StreamReader outFile = new StreamReader(tmpOutFile, encoding);
                                while ((line = outFile.ReadLine()) != null)
                                {
                                    // A conflict line always starts with <<<<<. If it doesn't, just print the line verbatim to output file
                                    if (line.StartsWith("<<<<<<<"))
                                    {
                                        // check if conflict section is empty
                                        bool firstline = true;
                                        // keep reading until a line starting with ==== is reached. After this line there is the target line with version, which we want to read and increase
                                        while ((line = outFile.ReadLine()) != null && !line.StartsWith("=======")) { }
                                        if (cc.ServerItem.ToLower().EndsWith(@"/assemblyinfo.cs"))
                                        {
                                            line = outFile.ReadLine();
                                            // if the conflict is not in AssemblyVersion, indicate an error
                                            if (!line.ToLower().StartsWith("[assembly: assemblyfileversion")) effedup = true;
                                            else
                                            {
                                                string num = line.ToLower().Split(new String[] { @"""" }, StringSplitOptions.None)[1].Split(new String[] { @"""" }, StringSplitOptions.None)[0];
                                                // if the version did not end with .9999, indicate an error
                                                if (!num.EndsWith(".9999")) effedup = true;
                                                else
                                                {
                                                    string[] numel = num.Split('.');
                                                    // if this is a special branch, increase the version at least by 1000
                                                    if (mergeObjectLink.ToPath.StartsWith("$/" + connection.GetTeamProject().Name  + "/" + Settings.GetInstance().SpecialVersioningBranch + "/") && Convert.ToInt32(numel[2]) < 1000) numel[2] = Convert.ToString(Convert.ToInt32(numel[2]) + 1001);
                                                    else numel[2] = Convert.ToString(Convert.ToInt32(numel[2]) + 1);
                                                    string ver = line = String.Format(@"{0}.{1}.{2}", numel[0], numel[1], numel[2]);

                                                    this.PrintMessageDuringMerge(String.Format("Setting version to {0}", ver));

                                                    line = String.Format(@"[assembly: AssemblyFileVersion(""{0}.9999"")]", ver);
                                                }
                                                File.AppendAllLines(tmpOutResultFile, new String[] { line }, encoding);
                                                // if the very next line is not the end of conflicting section, indicate an error
                                                while ((line = outFile.ReadLine()) != null && !line.StartsWith(">>>>>>>")) effedup = true;
                                            }
                                        }
                                        if (cc.ServerItem.ToLower().EndsWith(@".vbp"))
                                        {
                                            while ((line = outFile.ReadLine()) != null && !line.StartsWith(">>>>>>>"))
                                            {
                                                firstline = false;
                                                // if something else than version is in conflict, indicate an error
                                                if (!(line.ToLower().StartsWith("majorver=") || line.ToLower().StartsWith("minorver=") || line.ToLower().StartsWith("revisionver="))) effedup = true;
                                                if (line.ToLower().StartsWith("revisionver="))
                                                {
                                                    int num = Convert.ToInt32(line.Split('=')[1]);
                                                    // if this is a special branch, increase the version at least by 1000
                                                    if (mergeObjectLink.ToPath.StartsWith("$/" + connection.GetTeamProject().Name + "/" + Settings.GetInstance().SpecialVersioningBranch + "/") && num < 1000) num = num + 1001;
                                                    else num = num + 1;
                                                    line = String.Format("RevisionVer={0}", num);

                                                    this.PrintMessageDuringMerge(String.Format("Setting RevisionVer to {0}", num), _passedColor);
                                                }
                                                File.AppendAllLines(tmpOutResultFile, new String[] { line }, encoding);
                                            }
                                            if (firstline) effedup = true;
                                        }
                                        if (cc.ServerItem.ToLower().EndsWith(@".rc"))
                                        {
                                            while ((line = outFile.ReadLine()) != null && !line.StartsWith(">>>>>>>"))
                                            {
                                                firstline = false;
                                                // if something else than version is in conflict, indicate an error
                                                // we don't care for ProductVersion or FileVersion in StringFileInfo, since it's being overwritten by the build system
                                                if (line.Trim().ToLower().StartsWith("fileversion "))
                                                {
                                                    string num = line.ToLower().Split(new String[] { "fileversion " }, StringSplitOptions.None)[1].Trim();
                                                    string[] numel = num.Replace(',', '.').Split('.');
                                                    // if this is a special branch, increase the version at least by 1000
                                                    if (mergeObjectLink.ToPath.StartsWith("$/" + connection.GetTeamProject().Name + "/" + Settings.GetInstance().SpecialVersioningBranch + "/") && Convert.ToInt32(numel[2]) < 1000) numel[2] = Convert.ToString(Convert.ToInt32(numel[2]) + 1001);
                                                    else numel[2] = Convert.ToString(Convert.ToInt32(numel[2]) + 1);

                                                    string ver = line = String.Format(@"{0}.{1}.{2}", numel[0], numel[1], numel[2]);

                                                    this.PrintMessageDuringMerge(String.Format("Setting version to {0}", ver));

                                                    line = String.Format(" FILEVERSION {0},9999", ver.Replace('.', ','));
                                                }
                                                else if (line.Trim().ToLower().StartsWith("productversion ")) line = " PRODUCTVERSION 0,0,0,0";
                                                else if (line.Trim().ToLower().StartsWith(@"value ""fileversion""")) line = String.Format(@"            VALUE ""FileVersion"", ""0.0.0.0{0}""", line.EndsWith(@"\0""") ? @"\0" : String.Empty);
                                                else if (line.Trim().ToLower().StartsWith(@"value ""productversion""")) line = String.Format(@"            VALUE ""ProductVersion"", ""0.0.0.0{0}""", line.EndsWith(@"\0""") ? @"\0" : String.Empty);
                                                else effedup = true;
                                                File.AppendAllLines(tmpOutResultFile, new String[] { line }, encoding);
                                            }
                                            if (firstline) effedup = true;
                                        }
                                        if (cc.ServerItem.ToLower().EndsWith(@".sql"))
                                        {
                                            while ((line = outFile.ReadLine()) != null && !line.StartsWith(">>>>>>>"))
                                            {
                                                firstline = false;
                                                // if something else than version is in conflict, indicate an error
                                                if (line.ToLower().Contains("pr_object_version"))
                                                {
                                                    string ver = line.Split(',')[1].Split('\'')[1];
                                                    string oldver = ver;
                                                    string[] numel = ver.Split('.');
                                                    if (numel.Length == 3)
                                                    {
                                                        // if version is three digits, convert to four digits
                                                        ver = String.Format("{0}.{1}.0.{2}", numel[0], numel[1], numel[2]);
                                                        numel = ver.Split('.');
                                                    }
                                                    // if this is a special branch, increase the version at least by 1000
                                                    if (mergeObjectLink.ToPath.StartsWith("$/" + connection.GetTeamProject().Name + "/" + Settings.GetInstance().SpecialVersioningBranch + "/") && Convert.ToInt32(numel[3]) < 1000) numel[3] = Convert.ToString(Convert.ToInt32(numel[3]) + 1001);
                                                    else numel[3] = Convert.ToString(Convert.ToInt32(numel[3]) + 1);
                                                    ver = String.Format("{0}.{1}.{2}.{3}", numel[0], numel[1], numel[2], numel[3]);

                                                    this.PrintMessageDuringMerge(String.Format("Setting version to {0}", ver));

                                                    line = line.Replace(oldver, ver);
                                                    File.AppendAllLines(tmpOutResultFile, new String[] { line }, encoding);
                                                }
                                                else effedup = true;
                                            }
                                            if (firstline) effedup = true;
                                        }
                                    }
                                    else
                                    {
                                        File.AppendAllLines(tmpOutResultFile, new String[] { line }, encoding);
                                    }
                                }
                                outFile.Close();
                                c.MergedFileName = tmpOutResultFile;
                            }
                            catch (Exception ex)
                            {
                                effedup = true;
                                this.PrintMessageDuringMerge(String.Format("{0}\r\n{1}", ex.Message, ex.InnerException.Message),Brushes.Red);
                            }
                        }
                        if (!effedup)
                        {
                            c.Resolution = Resolution.AcceptMerge;
                            mergeObjectLink.TempWorkspace.Workspace.ResolveConflict(c);
                        }
                        else
                        {
                            this.PrintMessageDuringMerge(String.Format("Unable to solve the conflict in {0}", c.ServerPath), Brushes.Red);
                        }
                    }
                }
            }
            cer = mergeObjectLink.TempWorkspace.Workspace.EvaluateCheckin2(CheckinEvaluationOptions.All, 
                mergeObjectLink.TempWorkspace.Workspace.GetPendingChanges(), mergeObjectLink.Comment, null, mergeObjectLink.WorkItemsToAssociate.ToArray());

            if (cer.Conflicts.Length != 0)
            {
                ResolveConflictsDialog rd = new ResolveConflictsDialog(mergeObjectLink.TempWorkspace.Workspace);
                rd.ShowDialog();
            }

            cer = mergeObjectLink.TempWorkspace.Workspace.EvaluateCheckin2(CheckinEvaluationOptions.All,
                mergeObjectLink.TempWorkspace.Workspace.GetPendingChanges(), mergeObjectLink.Comment, null, mergeObjectLink.WorkItemsToAssociate.ToArray());

            if (cer.Conflicts.Length != 0)
            {
                mergeObjectLink.InterruptMessage = "User has cancelled resolution of conflicts - skipping!";
            }

            return mergeObjectLink;
        }

        public DelegateCommand StartMerge { get; private set; } 
        
        public DelegateCommand<object> BaselessBranchesCheck { get; private set; }

        public DelegateCommand<object> UnavailableBranchNamesCheck { get; private set; }

        public DelegateCommand<object> NonUniqueBranchNamesCheck { get; private set; }

        public DelegateCommand<object> ChangesToMergeCheck { get; private set; } 
    }
}
